﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace SoulKeeper
{
    public class Player : Sprite
    {
        /// <summary>
        /// Enum que "controla"/"contém" as animações
        /// </summary>
        public enum animIndexes { Attack, Jump, Walk, Damage, Idle, Fall , Walk2 };
        public int player;

        public Rectangle scrollArea;

        public Player(Texture2D image, GameWindow Window): base(image, Window)
        {
            controlAnimations();

            this.lastPosition = new Vector2(50, 600);
            this.position = new Vector2(50, 600);
            this.velocity = Vector2.Zero;
            this.maxVelocity = new Vector2(5,15);
            this.walkSpeed = 0.5f;
            this.jumpPower = 1.4f;
            this.dy = 0.0f;
            
            this.player = 1;
            this.scrollArea = new Rectangle(150, 100, 500, 400);

            
            IsMoving = false;
            IsFalling = false;
            IsJumping = false;
            canJump = true;
            IsAffectedByGravity = true;
        }//Construtor

        
        /// <summary>
        /// Define as animações do personagem
        /// </summary>
        private void controlAnimations()
        {
            //Attack//
            Animation attack = new Animation();

            //Jump//
            Animation jump = new Animation();

            //Walk//
            Animation walk = new Animation();
            walk.frames = 6;
            walk.fps = 12;
            walk.Y = 51;
            walk.width = 50;
            walk.height = 43;

            //Walk2//
            Animation walk2 = new Animation();
            walk2.frames = 6;
            walk2.fps = 6;
            walk2.Y = 99;
            walk2.width = 50;
            walk2.height = 43;

            //Damage//
            Animation damage = new Animation();

            //Idle//
            Animation idle = new Animation();
            idle.frames = 4;
            idle.fps = 4;
            idle.Y = 0;
            idle.width = 42;
            idle.height = 47;

            //fall//
            Animation fall = new Animation();
            fall.frames = 2;
            fall.fps = 2;
            fall.Y = 958;
            fall.width = 46;
            fall.height = 54;

            this.animations.Add(attack);
            this.animations.Add(jump);
            this.animations.Add(walk);
            this.animations.Add(damage);
            this.animations.Add(idle);
            this.animations.Add(fall);
            this.animations.Add(walk2);

            this.setAnimationIndex((int) Player.animIndexes.Idle);
            this.BoundingBox = new Rectangle((int)position.X, (int)position.Y, idle.width, idle.height);
            
         }//ControlAnimations
                
        /// <summary>
        /// Update do objeto
        /// </summary>
        /// <param name="gameTime">Para sincronizar o tempo do jogo como um todo</param>
        public override void Update(GameTime gameTime)
        { 
           controlMovement();           
          
        }//Update

        /// <summary>
        ///Controle de movimento do personagem do Jogador 1 ( nao pensamos em multplayer ainda ) 
        /// </summary>
        public void controlMovement()
        {
            float dt = (float)Engine.gameTime.ElapsedGameTime.Milliseconds;
            float verticalChange = (this.dy * dt) + (dt * Level.gravity);

            if (verticalChange > 20.0f)
            {
                verticalChange = 20.0f;
            }

            this.dy += (float)(dt * Level.gravity);

            Vector2 movement = Vector2.Zero;
            this.isMoving = false;
            this.setAnimationIndex((int)Player.animIndexes.Idle);

            if (player == 1)
            {
                #region MovePlayer
                if (Engine.keyboard.IsKeyDown(Keys.Right))
                {
                    movement.X += dt * walkSpeed;
                    this.isMoving = true;
                    this.facingRight = true;
                }
                
                if (Engine.keyboard.IsKeyDown(Keys.Left))
                {
                    movement.X -= dt * walkSpeed;
                    this.isMoving = true;
                    this.facingRight = false;
                }

                if (Engine.keyboard.IsKeyDown(Keys.Up) && !Engine.last_keyboard.IsKeyDown(Keys.Up))
                {
                    if (canJump && !(IsJumping && IsFalling))
                    {
                        IsFalling = false;
                        IsJumping = true;
                        canJump = false;
                        this.dy = -this.jumpPower;
                        verticalChange = -this.jumpPower;
                    }
                }

                movement.Y = (float)Math.Ceiling(verticalChange);
                #endregion

                #region JumpPlayer

                if (isMoving)
                    this.setAnimationIndex((int)Player.animIndexes.Walk);
                
                if (isJumping)
                    this.setAnimationIndex((int)Player.animIndexes.Fall);

                #endregion
                #region EndUpdateVariables
                Animation currentAnim = this.getAnimation();
                Vector2 futurePosition = clampToWorld(currentAnim, new Vector2(Position.X + movement.X, Position.Y + movement.Y));
                this.BoundingBox = new Rectangle((int)futurePosition.X, (int)futurePosition.Y, currentAnim.width, currentAnim.height);
              
                #endregion
                
                checkCollisions(futurePosition, currentAnim);//Checador de Colisão

                //Ajustando o pulo
                if (IsJumping)
                {
                    if (!IsFalling && Math.Abs(LastPosition.Y - futurePosition.Y) <= 0.1)
                    {
                        IsFalling = true;
                    }
                    else if (IsFalling && Math.Abs(LastPosition.Y - Position.Y) <= 0.1)
                    {
                        IsFalling = false;
                        IsJumping = false;
                        canJump = true;
                    }
                }

                LastPosition = Position;
            }

            if ((Engine.mouse.LeftButton == ButtonState.Pressed))
            {
                if (Engine.last_mouse.LeftButton != ButtonState.Pressed)
                {
                    //efeitoSonoro.Play();
                }
            }

        }//controlMovement

        /// <summary>
        /// Essa funcao checara se acontece ou nao colisao com o player
        /// </summary>
        /// <param name="futurePosition">Ele pega a possivel posicao futura atraves de um Vector2</param>
        /// <param name="currentAnim">Ele pega qual e a animacao que esta acontecendo no momento da colisao</param>
        public void checkCollisions(Vector2 futurePosition, Animation currentAnim)
        {
            #region PositionVariables
            Vector2 copyFuturePosition = futurePosition;
            
            int currentX = TileMap.GetSquareByPixelX((int) (futurePosition.X + RelativeCenter.X));
            int startX = currentX - 1;
            int endX = currentX + 1;

            int currentY = TileMap.GetSquareByPixelY((int) (futurePosition.Y + RelativeCenter.Y));
            int startY = currentY - 1;
            int endY = currentY + 1;

            Vector2 distance = Vector2.Zero;
            #endregion

            #region RealCheck
            for (int x = startX; x <= endX; x++)
            {
                for (int y = startY; y <= endY; y++)
                {
                    if ((x >= 0) && (y >= 0) && (x < TileMap.MapWidth) && (y < TileMap.MapHeight))
                    {
                        Tile tile = TileMap.tiles[TileMap.GetTileAtSquare(x, y)];

                        if (tile.Collidable)
                        {
                            distance = Collision.GetIntersection(BoundingBox, TileMap.SquareWorldRectangle(x, y));

                            if (tile.Diagonal)
                            {
                                if (Math.Abs(distance.X) >= Math.Abs(distance.Y))
                                    futurePosition = new Vector2(futurePosition.X, futurePosition.Y + distance.Y);
                                else
                                    futurePosition = new Vector2(futurePosition.X + distance.X, futurePosition.Y);
                                
                            }
                            else if (tile.Horizontal)
                            {
                                futurePosition = new Vector2(futurePosition.X + distance.X, futurePosition.Y);
                            }
                            else
                            {
                                futurePosition = new Vector2(futurePosition.X, futurePosition.Y + distance.Y);
                            }

                            this.BoundingBox = new Rectangle((int)futurePosition.X, (int)futurePosition.Y, currentAnim.width, currentAnim.height);
                        }
                    }
                }
            }
            #endregion

            updateCamera(futurePosition - Position);
            Position = futurePosition;
        }//checkCollisions

        /// <summary>
        /// Mantém o jogador no retângulo (WorldRectangle, no caso dentro do "mapa")
        /// </summary>
        /// <param name="current">Ele precisa saber a animacao atual para fazer as colisoes e checar se ele saiu da tela</param>
        /// <param name="futurePosition"></param>
        /// <returns></returns>
        public Vector2 clampToWorld(Animation current, Vector2 futurePosition)
        {
            float posX = MathHelper.Clamp(futurePosition.X, 0, Camera.WorldRectangle.Right - current.width);
            float posY = MathHelper.Clamp(futurePosition.Y, 0, Camera.WorldRectangle.Bottom - current.height);
            return (new Vector2(posX, posY));
        }//clampToWorld

        /// <summary>
        /// Essa é a funcao que movimenta o que vemos , no caso ela representa a movimentação de nossa camera
        /// </summary>
        /// <param name="movement"></param>
        public void updateCamera(Vector2 movement)
        {
            //Estamos no canto esquerdo da tela
            if (Camera.Position.X == Camera.WorldRectangle.X && movement.X > 0) 
            {
                if (this.ScreenCenter.X >= Camera.ViewPortWidth/2)
                    Camera.Move(new Vector2(movement.X, 0));
            }
            //Estamos no canto direito da tela
            else if (Camera.Position.X == (Camera.WorldRectangle.Width - Camera.ViewPortWidth) && movement.X < 0)
            {
                if (this.ScreenCenter.X <= Camera.ViewPortWidth / 2)
                    Camera.Move(new Vector2(movement.X, 0));
            }
            else
            {
                Camera.Move(new Vector2(movement.X, 0));
            }
        }//updateCamera
    }
}
